package com.example.demo.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import sun.java2d.opengl.OGLContext;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author LiYuhang
 * @version 0.1
 * @application
 * @Date 2020/12/17 11:35
 */
@Aspect
@Component
public class ExceptionAspect {
    private static final Logger log = LoggerFactory.getLogger(ExceptionAspect.class);
    private static ThreadLocal<ProceedingJoinPoint> threadLocal = new ThreadLocal<>();
    /**
     * 日志跟踪id名。
     */
    public static final String LOG_TRACE_ID = "X-B3-TraceId";

    /**
     * 切所有controller请求
     */
    @Pointcut("execution(* com.example.demo.controller..*.*(..))")
    @Order(1)
    public void pointCut1() {
    }
    /**
     * 切所有的抛出异常
     */
    @Pointcut("execution(* com.example.demo..*.*(..))")
    @Order(2)
    public void pointCut2() {
    }
    @Before("pointCut1()")
    public void doBefore(JoinPoint point) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        String uuid=generatorUuid();
        MDC.put(LOG_TRACE_ID,uuid);
        String reqParm=null;
        try {
            reqParm=Stream.of(point.getArgs()).map(v->v.toString()).collect(Collectors.joining("|"));
        } catch (Exception e) {
            log.error("打印入参异常{}",uuid);
        }
        log.info("\n请求信息: url {} method {} 入参 {}",request.getRequestURL(),method.getName(),reqParm);
    }

    @Around("pointCut1()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        threadLocal.set(point);
        long startTime = System.currentTimeMillis();
        ProceedingJoinPoint joinPoint = threadLocal.get();
        Object proceed = joinPoint.proceed();
        if (proceed != null) {
            try {
                // 打印出参
                log.info("Response Args  : {}", JSON.parseObject(proceed.toString()));
            } catch (Exception e) {
                String uuid = MDC.get(LOG_TRACE_ID);
                log.error("打印出参异常{}",uuid);
            }
        }
        // 执行耗时
        log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
        return proceed;
    }

    @AfterReturning("pointCut1()")
    public void doAfter() throws Throwable {
        // 接口结束后换行，方便分割查看
        log.info("=========================================== End ===========================================" );
        MDC.remove(LOG_TRACE_ID);
    }


    @AfterThrowing(throwing = "throwable", pointcut = "pointCut2()")
    public void recordException(JoinPoint point,Throwable throwable) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        String uuid = MDC.get(LOG_TRACE_ID);
        log.error("\n请求URL:{}",request.getRequestURL());
        log.error("异常信息:tranceId:{}, {}.{}.({}) : message:{}",uuid,method.getDeclaringClass().getName(),method.getName(),point.getArgs(),throwable.getMessage(),throwable);
    }

    public String generatorUuid() {
        String uuid = UUID.randomUUID().toString();
        uuid = uuid.replace("-","");
        String id = uuid + "-" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        return id.toUpperCase();
    }

}
